/**HEADER********************************************************************
* 
* Copyright (c) 2011 Freescale Semiconductor;
* All Rights Reserved
*
*************************************************************************** 
*
* THIS SOFTWARE IS PROVIDED BY FREESCALE "AS IS" AND ANY EXPRESSED OR 
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  
* IN NO EVENT SHALL FREESCALE OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
* THE POSSIBILITY OF SUCH DAMAGE.
*
**************************************************************************
*
* $FileName: lwgpio_mcf51em.c$
* $Version : 3.8.3.0$
* $Date    : Sep-23-2011$
*
* Comments:
*
*   This file contains the GPIO standard functions used on boards
*
*END************************************************************************/

#include "mqx.h"
#include "bsp.h"
#include "lwgpio.h"

/*** FLAGS for LWGPIO_STRUCT.flags ***/

/**************************************************************************
 * int.mask * int.src * direction * int.mode * port number  *  pin index  *
 *  8bits   *  2bits  *   1 bit   *   3bits  *    8bits     *    3bits    *
 **************************************************************************/

/* - pin index */
#define LWGPIO_PIN_SHIFT (0)
#define LWGPIO_PIN_MASK (0x7 << LWGPIO_PIN_SHIFT)
#define LWGPIO_PIN(x) (((x) << LWGPIO_PIN_SHIFT) & LWGPIO_PIN_MASK)
#define LWGPIO_PIN_FROM_ID(id) (((id) & LWGPIO_PIN_MASK) >> LWGPIO_PIN_SHIFT)

/* - port number */
#define LWGPIO_PORT_SHIFT (3)
#define LWGPIO_PORT_MASK (0xFF << LWGPIO_PORT_SHIFT)
#define LWGPIO_PORT(x) (((x) << LWGPIO_PORT_SHIFT) & LWGPIO_PORT_MASK)
#define LWGPIO_PORT_FROM_ID(id) (((id) & LWGPIO_PORT_MASK) >> LWGPIO_PORT_SHIFT)

/* - int. mode */
#define LWGPIO_INT_MODE_SHIFT (11)
#define LWGPIO_INT_MODE_MASK (0x03 << LWGPIO_INT_MODE_SHIFT)
#define LWGPIO_INT_MODE(x) (((x) << LWGPIO_INT_MODE_SHIFT) & LWGPIO_INT_MODE_MASK)
#define LWGPIO_INT_MODE_FROM_ID(id) (((id) & LWGPIO_INT_MODE_MASK) >> LWGPIO_INT_MODE_SHIFT)

/* - direction */ 
/*
#define LWGPIO_DIR_SHIFT (13)
#define LWGPIO_DIR_MASK (0x01 << LWGPIO_DIR_SHIFT)
*/

/* - int. source (KBI1, KBI2, IRQ pin) */
#define LWGPIO_INT_SRC_SHIFT (14)
#define LWGPIO_INT_SRC_MASK (0x03 << LWGPIO_INT_SRC_SHIFT)
#define LWGPIO_INT_SRC(x) (((x) << LWGPIO_INT_SRC_SHIFT) & LWGPIO_INT_SRC_MASK)
#define LWGPIO_INT_SRC_FROM_ID(id) (((id) & LWGPIO_INT_SRC_MASK) >> LWGPIO_INT_SRC_SHIFT) 

/* - int. mask */
#define LWGPIO_INT_PINMASK_SHIFT (16)
#define LWGPIO_INT_PINMASK_MASK (0xFF << LWGPIO_INT_PINMASK_SHIFT)
#define LWGPIO_INT_PINMASK(x) (((x) << LWGPIO_INT_PINMASK_SHIFT) & LWGPIO_INT_PINMASK_MASK)
#define LWGPIO_INT_PINMASK_FROM_ID(id) (((id) & LWGPIO_INT_PINMASK_MASK) >> LWGPIO_INT_PINMASK_SHIFT) 

/* interrupt sources */
#define LWGPIO_INT_SRC_KBI1    (0)
#define LWGPIO_INT_SRC_KBI2    (1)
#define LWGPIO_INT_SRC_IRQ     (2)

/* */
#define LWGPIO_PTA_IRQ_MASK    (0x01)
#define LWGPIO_PTB_KBI1A_MASK  (0xC0)
#define LWGPIO_PTB_KBI1B_MASK  (0x0F)
#define LWGPIO_PTC_KBI1_MASK   (0x03)

#define LWGPIO_IRQSC_ES        (0x20)
#define LWGPIO_IRQSC_PE        (0x10)

/*** GLOBALS ***/

/* ports */
static const VMCF51EM_GPIO_PORTS_STRUCT_PTR lwgpio_ports_struct = (VMCF51EM_GPIO_PORTS_STRUCT_PTR) &(((VMCF51EM_STRUCT_PTR)BSP_IPSBAR)->GPIO);

/* pointers to interrupt status registers - sorted according to LWGPIO_INT_SRC_* */
static volatile uchar * lwgpio_int_regsc[] = {
    &(((VMCF51EM_STRUCT_PTR)BSP_IPSBAR)->GPIO.KBI1SC),
    &(((VMCF51EM_STRUCT_PTR)BSP_IPSBAR)->GPIO.KBI2SC),
    &(((VMCF51EM_STRUCT_PTR)BSP_IPSBAR)->GPIO.IRQSC)
};

/* interrupt vectors - sorted according to LWGPIO_INT_SRC_* */
static const MCF51EM_INTERRUPT_TABLE_INDEX lwgpio_int_vectors[] = {
    MCF51EM_INT_Vkeyboard1,
    MCF51EM_INT_Vkeyboard2,
    MCF51EM_INT_Virq
};

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_init
* Returned Value   : TRUE if succesfull, FALSE otherwise
* Comments         :
*    Decodes ID to HW specific struct and then performs pin initialization
*
*END*********************************************************************/
boolean lwgpio_init
(
    /* Pointer to LWGPIO internal structure to be filled in */
    LWGPIO_STRUCT_PTR handle,
    /* Pin ID, bitmask integer value */
    LWGPIO_PIN_ID     id,
    /* Direction to be set within initialization */
    LWGPIO_DIR        dir,
    /* Value to be set within initialization */
    LWGPIO_VALUE      value
)
{ /* Body */

    uint_32 port_idx, pin_idx;
    
    port_idx = LWGPIO_PORT_FROM_ID(id);
    pin_idx = LWGPIO_PIN_FROM_ID(id);

    handle->port_idx = port_idx;
    handle->pinmask = 1 << pin_idx;
    handle->flags = id;        

    /* Set value prior to set to output */
    if (value != LWGPIO_VALUE_NOCHANGE) {
        /* Note: there is no check for values not defined as LWGPIO_VALUE enum */
        lwgpio_set_value(handle, value);
    }

    if (dir != LWGPIO_DIR_NOCHANGE) {
        /* Note: there is no check for values not defined as LWGPIO_DIR enum */
        lwgpio_set_direction(handle, dir);
    }

    return TRUE;
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_set_attribute
* Returned Value   : TRUE if succesfull, FALSE otherwise
* Comments         :
*    Sets attributes
*
*END*********************************************************************/
boolean lwgpio_set_attribute
(
    /* Pin handle to get function from */
    LWGPIO_STRUCT_PTR  handle,
    /* PORT attribute */
    uint_32 attribute_id,
    /* Attribute value */
    uint_32 value
)
{
    switch(attribute_id)
    {
        case(LWGPIO_ATTR_PULL_UP):
        {
            if(value == LWGPIO_AVAL_ENABLE)
            {
                lwgpio_ports_struct->PORT[ handle->port_idx ].PTxPE |= handle->pinmask;
            }
            else        /* value == LWGPIO_AVAL_DISABLE */
            {
                lwgpio_ports_struct->PORT[ handle->port_idx ].PTxPE &= ~(handle->pinmask);
            }
            break;
        }
        case(LWGPIO_ATTR_SLEW_RATE):
        {
            if(value == LWGPIO_AVAL_ENABLE)
            {
                lwgpio_ports_struct->PORT[ handle->port_idx ].PTxSE |= handle->pinmask;
            }
            else        /* value == LWGPIO_AVAL_DISABLE */
            {
                lwgpio_ports_struct->PORT[ handle->port_idx ].PTxSE &= ~(handle->pinmask);
            }
            break;
        }
        case(LWGPIO_ATTR_FILTER):
        {
            if(value == LWGPIO_AVAL_ENABLE)
            {
                lwgpio_ports_struct->PORT[ handle->port_idx ].PTxIFE |= handle->pinmask;
            }
            else        /* value == LWGPIO_AVAL_DISABLE */
            {
                lwgpio_ports_struct->PORT[ handle->port_idx ].PTxIFE &= ~(handle->pinmask);
            }
            break;
        }
        case(LWGPIO_ATTR_DRIVE_STRENGTH):
        {
            if(value == LWGPIO_AVAL_DRIVE_STRENGTH_HIGH)
            {
                lwgpio_ports_struct->PORT[ handle->port_idx ].PTxDS |= handle->pinmask;
            }
            else        /* value == LWGPIO_AVAL_DRIVE_STRENGTH_LOW */
            {
                lwgpio_ports_struct->PORT[ handle->port_idx ].PTxDS &= ~(handle->pinmask);
            }
            break;
        }
        default:
        {
            return FALSE;
        }
    }
    return TRUE;
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_set_functionality
* Returned Value   : void
* Comments         :
*    Sets functionality (peripheral mode) of the pin
*
*END*********************************************************************/
void lwgpio_set_functionality
(
    /* Pin handle to set function on */
    LWGPIO_STRUCT_PTR  handle, 
    /* Function to be set (integer value) */
    uint_32            function
)
{
    /*
    
    static const VMCF51EM_GPIO_PORTS_STRUCT_PTR lwgpio_ports_struct;
    
    lwgpio_ports_struct = ((VMCF51EM_GPIO_PORTS_STRUCT_PTR) &(((VMCF51EM_STRUCT_PTR)BSP_IPSBAR )->GPIO));
    
    ***
                 
    lwgpio_ports_struct->PTxPF[ 0 ]
    
    and
    
    ((VMCF51EM_STRUCT_PTR)BSP_IPSBAR)->GPIO.PTAPF
    
    should point to the same memory place but it doesn't
    
    ***
    
    */
    
    uint_16 clear_mask = 0x03 << LWGPIO_PIN_FROM_ID(handle->flags) * 2;	
    uint_16 funct_mask = (function & 0x03) << LWGPIO_PIN_FROM_ID(handle->flags) * 2;

    volatile uint_16 * pf_reg = &(((VMCF51EM_STRUCT_PTR)BSP_IPSBAR)->GPIO.PTAPF) + handle->port_idx;
    
    *pf_reg &= ~clear_mask;
    *pf_reg |= funct_mask;
        
    return;
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_get_functionality
* Returned Value   : void
* Comments         :
*    Gets function (peripheral mode) of the pin
*
*END*********************************************************************/
uint_32 lwgpio_get_functionality
(
    /* Pin handle to get function from */
    LWGPIO_STRUCT_PTR  handle
)
{
    return ( lwgpio_ports_struct->PTxPF[ handle->port_idx ] >> LWGPIO_PIN_FROM_ID(handle->flags) * 2 ) & 0x03;
}


/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_set_direction
* Returned Value   : void
* Comments         :
*    Sets direction of the pin
*
*END*********************************************************************/
void lwgpio_set_direction
(
    /* Pin handle to set direction on */
    LWGPIO_STRUCT_PTR  handle, 
    /* Direction to be set */
    LWGPIO_DIR         dir
)
{
    if ( dir == LWGPIO_DIR_INPUT )
    {
        lwgpio_ports_struct->PORT[ handle->port_idx ].PTxDD &= ~handle->pinmask;
    }
    else if ( dir == LWGPIO_DIR_OUTPUT )
    {
        lwgpio_ports_struct->PORT[ handle->port_idx ].PTxDD |= handle->pinmask;
    }
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_set_value
* Returned Value   : void
* Comments         :
*    Sets value (output latch) of the pin
*
*END*********************************************************************/
void lwgpio_set_value
(
    /* Pin handle to set value on */
    LWGPIO_STRUCT_PTR  handle, 
    /* Direction to be set */
    LWGPIO_VALUE       out_value
)
{
    if ( out_value == LWGPIO_VALUE_LOW ) {
        lwgpio_ports_struct->PORT[ handle->port_idx ].PTxCLR = handle->pinmask;
    }
    else if ( out_value == LWGPIO_VALUE_HIGH ) {
        lwgpio_ports_struct->PORT[ handle->port_idx ].PTxSET = handle->pinmask;
    }

}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_toggle_value
* Returned Value   : void
* Comments         :
*    Toggles value of output latch of the pin
*
*END*********************************************************************/
void lwgpio_toggle_value
(
    /* Pin handle to toggle value on */
    LWGPIO_STRUCT_PTR  handle
)
{
    lwgpio_ports_struct->PORT[ handle->port_idx ].PTxTOG = handle->pinmask;
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_get_value
* Returned Value   : LWGPIO_VALUE of pin status
* Comments         :
*    Returns value (output latch or read data) of the pin
*
*END*********************************************************************/
LWGPIO_VALUE lwgpio_get_value
(
    /* Pin handle to get value from */
    LWGPIO_STRUCT_PTR  handle
)
{
    return ( lwgpio_ports_struct->PORT[ handle->port_idx ].PTxD & handle->pinmask ) ? LWGPIO_VALUE_HIGH : LWGPIO_VALUE_LOW;
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_get_raw
* Returned Value   : LWGPIO_VALUE of pin status
* Comments         :
*    Returns read level of the pin
*
*END*********************************************************************/
LWGPIO_VALUE lwgpio_get_raw
(
    /* Pin handle to get value from */
    LWGPIO_STRUCT_PTR  handle
)
{
    return ( lwgpio_ports_struct->PORT[ handle->port_idx ].PTxD & handle->pinmask ) ? LWGPIO_VALUE_HIGH : LWGPIO_VALUE_LOW;
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_int_init
* Returned Value   : TRUE if succesfull, FALSE otherwise
* Comments         :
*    Initializes pin to generate interrupt
*
*END*********************************************************************/
boolean lwgpio_int_init
(
    /* Pin handle to initialize interrupt on */
    LWGPIO_STRUCT_PTR handle,
    /* Interrupt mode */
    LWGPIO_INT_MODE   mode
)
{
    uchar int_src  = 0;
    uchar reg_es_edg, reg_pe_pe;

    uchar int_enabled;

    volatile uchar * reg_es[] = {
        &(((VMCF51EM_STRUCT_PTR)BSP_IPSBAR)->GPIO.KBI1ES),
        &(((VMCF51EM_STRUCT_PTR)BSP_IPSBAR)->GPIO.KBI2ES),
        &(((VMCF51EM_STRUCT_PTR)BSP_IPSBAR)->GPIO.IRQSC)
    };
    
    volatile uchar * reg_pe[] = {
        &(((VMCF51EM_STRUCT_PTR)BSP_IPSBAR)->GPIO.KBI1PE),
        &(((VMCF51EM_STRUCT_PTR)BSP_IPSBAR)->GPIO.KBI2PE),
        &(((VMCF51EM_STRUCT_PTR)BSP_IPSBAR)->GPIO.IRQSC)
    };
        
    switch( handle->port_idx ) {
        
        case GPIO_PORT_A:
            if ( handle->pinmask != LWGPIO_PTA_IRQ_MASK )
                return FALSE;
            
            reg_es_edg = LWGPIO_IRQSC_ES;
            reg_pe_pe  = LWGPIO_IRQSC_PE;
            int_src    = LWGPIO_INT_SRC_IRQ;
            break;
        
        case GPIO_PORT_B:

            if ( handle->pinmask & LWGPIO_PTB_KBI1A_MASK )
                reg_es_edg = reg_pe_pe = handle->pinmask >> 2; 
    
            else if ( handle->pinmask & LWGPIO_PTB_KBI1B_MASK )
                reg_es_edg = reg_pe_pe = handle->pinmask;
            
            else
                return FALSE;
       
            int_src = LWGPIO_INT_SRC_KBI1;
            break;
    
        case GPIO_PORT_C:
            if ( handle->pinmask & ~LWGPIO_PTC_KBI1_MASK )
                return FALSE;
            
            reg_es_edg = reg_pe_pe = handle->pinmask << 6; 
            int_src = LWGPIO_INT_SRC_KBI1;
            break;
            
        case GPIO_PORT_D:
            reg_es_edg = reg_pe_pe = handle->pinmask;
            int_src = LWGPIO_INT_SRC_KBI2;
            break;
                
        default:
            return FALSE;
    }
    
    /* for the next code:
       MCF51XX_KBIXSC_KBIE, MCF51XX_KBIXSC_KBMOD and MCF51XX_KBIXSC_KBIE are
       KBIxSC pinmasks valid also for appropriate IRQSC pins */
    
    /* mask ints */
    int_enabled = *lwgpio_int_regsc[int_src] & MCF51XX_KBIXSC_KBIE_MASK;
    *lwgpio_int_regsc[int_src] &= ~MCF51XX_KBIXSC_KBIE_MASK; 
                             
    /* select pin polarity */
    if ( mode == LWGPIO_INT_MODE_FALLING || mode == LWGPIO_INT_MODE_LOW )        
        *reg_es[int_src] &= ~reg_es_edg;
    else if ( mode == LWGPIO_INT_MODE_RISING || mode == LWGPIO_INT_MODE_HIGH )    
        *reg_es[int_src] |= reg_es_edg;
    else {
        if( int_enabled )        
            *lwgpio_int_regsc[int_src] |= MCF51XX_KBIXSC_KBIE_MASK;
        return FALSE;
    }     
    /* want level detect? */              
    if( mode == LWGPIO_INT_MODE_LOW || mode == LWGPIO_INT_MODE_HIGH )
        *lwgpio_int_regsc[int_src] |= MCF51XX_KBIXSC_KBIMOD_MASK;
        
    
    /* pull up / pull down */    
    /* -> let the user do this */
    
    /* enable ints */
    *reg_pe[int_src] |= reg_pe_pe;
    
    /* clear false ints */
    *lwgpio_int_regsc[int_src] |= MCF51XX_KBIXSC_KBACK_MASK;
        
    handle->flags |= LWGPIO_INT_SRC( int_src );
    
    if( int_enabled )        
        *lwgpio_int_regsc[int_src] |= MCF51XX_KBIXSC_KBIE_MASK;
    
    return TRUE;
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_int_enable
* Returned Value   : void
* Comments         :
*    Enables / disables interrupts for specified pin
*
*END*********************************************************************/
void lwgpio_int_enable
(
    /* Pin handle to enable interrupt on */
    LWGPIO_STRUCT_PTR handle,
    /* Enable or disable interrupt? TRUE = enable */
    boolean           ena
)
{
    /* MCF51XX_KBIXSC_KBIE is valid IE pin mask also for IRQSC */
    if( ena )        
        *lwgpio_int_regsc[ LWGPIO_INT_SRC_FROM_ID(handle->flags) ] |= MCF51XX_KBIXSC_KBIE_MASK;
    else
        *lwgpio_int_regsc[ LWGPIO_INT_SRC_FROM_ID(handle->flags) ] &= ~MCF51XX_KBIXSC_KBIE_MASK;
    return;      
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_int_get_flag
* Returned Value   : TRUE if interrupt flag is set
* Comments         :
*    Checks if there is pending interrupt flag for specified pin
*
*END*********************************************************************/
boolean lwgpio_int_get_flag
(
    /* Pin handle to get interrupt flag on */
    LWGPIO_STRUCT_PTR handle
)
{
    /* MCF51XX_KBIXSC_KBF is valid interrupt flag pin mask also for IRQSC */ 
    return *lwgpio_int_regsc[ LWGPIO_INT_SRC_FROM_ID(handle->flags) ] & MCF51XX_KBIXSC_KBF_MASK ? TRUE : FALSE;    
}

/*FUNCTION*****************************************************************
* 
* Function Name    : lwgpio_int_clear_flag
* Returned Value   : void
* Comments         :
*    Clears pending interrupt flag on peripheral
*
*END*********************************************************************/
void lwgpio_int_clear_flag
(
    /* Pin handle to clear interrupt flag on */
    LWGPIO_STRUCT_PTR handle
)
{
    /* MCF51XX_KBIXSC_KBACK is valid ACK pin mask also for IRQSC */
    *lwgpio_int_regsc[ LWGPIO_INT_SRC_FROM_ID(handle->flags) ] |= MCF51XX_KBIXSC_KBACK_MASK;       
    return;   
}


/*FUNCTION****************************************************************
* 
* Function Name    : lwgpio_int_get_vector
* Returned Value   : vector index
* Comments         :
*    This function returns vector index for specified pin
*
*END*********************************************************************/
uint_32 lwgpio_int_get_vector
(
    /* Pin handle to get vector of */
    LWGPIO_STRUCT_PTR  handle
)
{
    return lwgpio_int_vectors[ LWGPIO_INT_SRC_FROM_ID(handle->flags) ];
}

/* EOF */
